package com.mqb;

import lombok.extern.slf4j.Slf4j;

import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author mqb
 * @date 2021/5/2 19:28
 */
@Slf4j(topic = "c.MyTest")
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        BlockQueueReentrantLock bq = new BlockQueueReentrantLock(3);
        log.debug("消费者启动");
        new Thread(() -> {
            while (true) {
                String take = bq.take();
                log.debug(Thread.currentThread().getName() + "消费了 {}", take);
            }
        }, "消费者1").start();
        new Thread(() -> {
            while (true) {
                String take = bq.take();
                log.debug(Thread.currentThread().getName() + "消费了 {}", take);
            }
        }, "消费者2").start();


        Thread.sleep(1000);
        log.debug("生产者启动");

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                bq.put("i" + i);
                log.debug("生产者1生产了 {}", "i" + i);
            }
        }, "生产者1").start();

    }

    private static void testSynchronized() throws InterruptedException {
        BlockingQueueWithSync bq = new BlockingQueueWithSync(3);

        log.debug("消费者启动");
        new Thread(() -> {
            while (true) {
                String take = bq.take();
                log.debug(Thread.currentThread().getName() + "消费了 {}", take);
            }
        }, "消费者1").start();
        new Thread(() -> {
            while (true) {
                String take = bq.take();
                log.debug(Thread.currentThread().getName() + "消费了 {}", take);
            }
        }, "消费者2").start();


        Thread.sleep(1000);
        log.debug("生产者启动");

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                bq.put("i" + i);
                log.debug("生产者1生产了 {}", "i" + i);
            }
        }, "生产者1").start();
    }
}

@Slf4j(topic = "c.BlockQueueSync")
class BlockingQueueWithSync {
    private int capacity;
    private LinkedList<String> queue = new LinkedList<>();

    public BlockingQueueWithSync(int capacity) {
        this.capacity = capacity;
    }

    public synchronized String take() {
        while (queue.isEmpty()) {
            log.debug("队列空的..");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        String s = queue.removeFirst();
        this.notifyAll();
        return s;
    }


    public synchronized void put(String element) {
        while (queue.size() == capacity) {
            log.debug("队列满了..");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        queue.addLast(element);
        this.notifyAll();
    }
}

@Slf4j(topic = "c.BlockQueueReentrantLock")
class BlockQueueReentrantLock {
    private int capacity;
    public final LinkedList<String> queue = new LinkedList<>();

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition fullWaitSet = lock.newCondition();
    private final Condition emptyWaitSet = lock.newCondition();

    public BlockQueueReentrantLock(int capacity) {
        this.capacity = capacity;
    }

    public String take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                log.debug("队列为空..");
                try {
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            String s = queue.removeFirst();
            fullWaitSet.signal();
            return s;
        } finally {
            lock.unlock();
        }
    }


    public void put(String element) {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                log.debug("队列为满..");
                try {
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            queue.addLast(element);
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }
}